package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static com.tianji.learning.enums.SectionType.VIDEO;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author 东哥
 * @since 2024-06-22
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {
    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;

    /**
     * 1、查询当前用户指定课程的学习进度（Day3 - 课堂）
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        log.info("查询当前用户指定课程：{} 的学习进度", courseId);
        // 1、根据userId 和 courseId 查询课表
        // select * from learning_lesson where user_id = #{userId} and course_id = #{courseId}
        LearningLesson lesson = lessonService.queryLessonByCourseIdAndUserId(courseId, UserContext.getUser());

        // 2、根据课表ID查询当前课表下的学习记录
        // select * from learning_record where lesson_id = #{lessonId}
        List<LearningRecord> records = this.lambdaQuery().eq(LearningRecord::getLessonId, lesson.getId()).list();

        // 3、组装DTO并返回
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(BeanUtils.copyList(records, LearningRecordDTO.class));
        return dto;
    }

    /**
     * 2、提交学习记录（Day3 - 课堂）
     *
     * @param recordDTO
     */
    @Override
    @Transactional
    public void addLearningRecord(LearningRecordFormDTO recordDTO) {
        // 1、处理学习提交记录
        /*boolean firstFinished;
        if(recordDTO.getSectionType() == VIDEO) {
            // 1.1、处理视频
            firstFinished = handlerVideoRecord(recordDTO);
        }else {
            // 1.2、处理考试
            firstFinished = handlerExamRecord(recordDTO);
        }*/

        boolean firstFinished = recordDTO.getSectionType() == VIDEO ? handlerVideoRecord(recordDTO) : handlerExamRecord(recordDTO);

        // 2、处理课表数据
        handlerLearningLesson(recordDTO,firstFinished);
    }

    /**
     * 处理课表数据
     * @param recordDTO 提交的学习记录
     * @param firstFinished 是否是第一次学完当前小节
     */
    private void handlerLearningLesson(LearningRecordFormDTO recordDTO, boolean firstFinished) {
        // 1、查询课表数据（已学习小节数、课程ID）
        LearningLesson lesson = this.lessonService.getById(recordDTO.getLessonId());
        AssertUtils.isNotNull(lesson,"课表数据不存在，无法更新数据");

        // 2、判断是否有新的完成小节
        boolean allLearned = false;
        if(firstFinished){
            // 2.1、查询课程数据
            CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            AssertUtils.isNotNull(courseInfo,"课程数据不存在，无法更新数据");

            // 2.2、比较课程是否已经全部学完（已学完小节数 + 1 >= 课程总的小节数）
            allLearned = lesson.getLearnedSections() + 1 >= courseInfo.getSectionNum();
        }

        // 3、更新课表数据
        boolean updateResult = this.lessonService.lambdaUpdate()
                .set(LearningLesson::getLatestSectionId, recordDTO.getSectionId())  // 最近学习的小节ID
                .set(LearningLesson::getLatestLearnTime, recordDTO.getCommitTime()) // 最近学习时间
                .setSql(firstFinished, "learned_sections = learned_sections + 1")   // 如果该小节已学完，将已学习小节数 + 1
                .set(lesson.getStatus() == LessonStatus.NOT_BEGIN, LearningLesson::getStatus, LessonStatus.LEARNING.getValue()) // 如果课表状态为未开始，则将课表状态修改为：学习中
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue()) // 如果该课程下的所有小节都已学完，则将课表状态修改为：已学完
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        if (!updateResult) {
            throw new DbException("修改课表信息失败！");
        }
    }

    /**
     * 处理视频学习记录
     *
     * @param recordDTO
     * @return
     */
    private boolean handlerVideoRecord(LearningRecordFormDTO recordDTO) {
        // 1、查询旧的学习记录
        LearningRecord oldRecord = lambdaQuery().eq(LearningRecord::getLessonId, recordDTO.getLessonId())
                .eq(LearningRecord::getSectionId, recordDTO.getSectionId()).one();

        // 2、判断学习记录是否已存在
        if (ObjectUtil.isEmpty(oldRecord)) {
            // 3、如果不存在，新增学习记录
            // 3.1、将DTO转换为PO
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);

            // 3.2、填充缺失的数据
            record.setUserId(UserContext.getUser());

            // 3.3、写入数据库
            boolean result = save(record);
            if (!result) {
                throw new DbException("新增学习记录失败！");
            }

            // 3.4、返回标识
            return false;
        }

        // 4、如果存在，更新学习记录
        // 4.1、判断是否是第一次学完当前小节
        boolean firstFinished = recordDTO.getMoment() << 1 >= recordDTO.getDuration() && !oldRecord.getFinished();

        // 4.2、更新学习记录（一定更新的：moment，可能更新的2个字段：finished，finish_time）
        boolean updateResult = this.lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment()) // 更新播放进度
                .set(firstFinished, LearningRecord::getFinished, true) // 更新状态为：已学完
                .set(firstFinished, LearningRecord::getFinishTime, recordDTO.getCommitTime()) //更新完成学习的时间
                .eq(LearningRecord::getId, oldRecord.getId())
                .update();
        if (!updateResult) {
            throw new DbException("更新学习记录失败！");
        }
        return firstFinished;
    }

    /**
     * 处理考试记录
     *
     * @param recordDTO
     * @return
     */
    private boolean handlerExamRecord(LearningRecordFormDTO recordDTO) {
        // 1、将DTO转换为PO
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);

        // 2、填充缺失的数据
        record.setUserId(UserContext.getUser());
        record.setFinished(true);
        record.setFinishTime(recordDTO.getCommitTime());

        // 3、写入数据库
        boolean result = save(record);
        if (!result) {
            throw new DbException("新增考试记录失败！");
        }

        // 4、返回标识
        return true;
    }
}
